package com.xingchi.tornado.unique.provider.impl;

import com.xingchi.tornado.unique.enums.IDProviderType;
import com.xingchi.tornado.unique.provider.IDProvider;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 简单ID生成器实现，基于原子计数器并加入时间戳
 * 支持分布式环境下的唯一性
 *      32bit       8bit        24bit
 *      时间戳      节点id       序列号
 *  时间戳大概支持139年，256台集群  每秒可产生大概1677w个id
 *
 * @author xingchi
 * @date 2025/5/9 15:24
 */
public class SimpleProvider implements IDProvider<Long> {

    /**
     * 原子计数器，用于生成序列号
     */
    private final AtomicLong counter = new AtomicLong(0);

    /**
     * 开始时间，单位s
     */
    private static final long TIME_EPOCH = 1675282963L;

    /**
     * 序列号位数
     */
    private static final int SEQUENCE_BITS = 24;

    /**
     * 节点ID位数
     */
    private static final int NODE_ID_BITS = 8;

    /**
     * 最大节点ID
     */
    private static final int MAX_NODE_ID = ~(-1 << NODE_ID_BITS);

    /**
     * 计数器最大值，超过此值会重置
     */
    private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;

    /**
     * 节点ID，用于区分不同服务器实例
     */
    private final int nodeId;

    /**
     * 序列号左移位数
     */
    private static final int NODE_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳左移位数
     */
    private static final int TIMESTAMP_SHIFT = SEQUENCE_BITS + NODE_ID_BITS;

    /**
     * 上一个时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 构造函数，自动生成节点ID
     */
    public SimpleProvider() {
        this.nodeId = generateNodeId();
    }

    /**
     * 构造函数，手动指定节点ID
     *
     * @param nodeId 节点ID
     */
    public SimpleProvider(int nodeId) {
        if (nodeId < 0 || nodeId > MAX_NODE_ID) {
            throw new IllegalArgumentException("节点ID必须在0~" + MAX_NODE_ID + "之间");
        }
        this.nodeId = nodeId;
    }

    /**
     * 生成节点ID，基于MAC地址和主机名
     *
     * @return 节点ID
     */
    private int generateNodeId() {
        try {
            // 获取MAC地址
            byte[] mac = getMacAddress();
            if (mac != null) {
                // 用最后两个字节生成节点ID
                return ((mac[mac.length - 2] & 0xFF) << 8
                      | (mac[mac.length - 1] & 0xFF)) % (MAX_NODE_ID + 1);
            }

            // 获取本机地址
            InetAddress localHost = InetAddress.getLocalHost();
            byte[] addressBytes = localHost.getAddress();
            return ((addressBytes[addressBytes.length - 2] & 0xFF) << 8
                  | (addressBytes[addressBytes.length - 1] & 0xFF)) % (MAX_NODE_ID + 1);
        } catch (Exception e) {
            // 降级到随机机制
            return (int) (System.currentTimeMillis() % (MAX_NODE_ID + 1));
        }
    }

    /**
     * 获取MAC地址
     *
     * @return MAC地址
     */
    private byte[] getMacAddress() throws SocketException {
        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements()) {
            NetworkInterface network = networkInterfaces.nextElement();
            byte[] mac = network.getHardwareAddress();
            if (mac != null && mac.length > 0 && !isVirtualInterface(network)) {
                return mac;
            }
        }
        return null;
    }

    /**
     * 判断网络接口是否为虚拟接口
     *
     * @param network 网络接口
     * @return 是否为虚拟接口
     */
    private boolean isVirtualInterface(NetworkInterface network) throws SocketException {
        return network.isVirtual() || network.isLoopback() || network.isPointToPoint()
               || !network.isUp() || network.getName().contains("virtual");
    }

    /**
     * 生成下一个ID
     *
     * @return 生成的ID
     */
    @Override
    public Long nextId() {
        // 获取当前时间，并计算当前时间与起始时间的差值
        long timestamp = tilNextMillis(lastTimestamp);

        if (timestamp < 0) {
            throw new RuntimeException("时钟回拨，请调整系统时间！");
        }

        // 获取序列号，如果超过最大值则重置
        long sequence = counter.incrementAndGet();
        if (sequence > MAX_SEQUENCE) {
            synchronized (this) {
                // 双重检查
                if (counter.get() > MAX_SEQUENCE) {
                    counter.set(0);
                    // 判断上一次重置的事件与此次重置的事件是否一致，如果一致则说明是同一秒内生成的id则等待下一秒
                    if (lastTimestamp == timestamp) {
                        timestamp = tilNextMillis(lastTimestamp);
                    }
                    lastTimestamp = timestamp;
                }
                sequence = counter.incrementAndGet();
            }
        }
        // 组合时间戳、节点ID和序列号
        return (timestamp << TIMESTAMP_SHIFT) | ((long) nodeId << NODE_ID_SHIFT) | sequence;
    }

    /**
     * 批量生成ID
     *
     * @param count 获取个数
     * @return ID列表
     */
    @Override
    public List<Long> nextIds(Integer count) {
        if (count == null || count <= 0) {
            count = DEFAULT_COUNT;
        }

        List<Long> result = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            result.add(this.nextId());
        }
        return result;
    }

    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        LocalDateTime now = LocalDateTime.now();
        return now.toEpochSecond(ZoneOffset.UTC) - TIME_EPOCH;
    }

    /**
     * 判断是否支持指定的ID生成器类型
     *
     * @param type id类型
     * @return 是否支持
     */
    @Override
    public boolean supports(IDProviderType type) {
        return IDProviderType.SIMPLE.equals(type);
    }
}
